package top.xzxsrq.fileviewutils.security;

import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import top.xzxsrq.fileviewutils.entity.SysUser;
import top.xzxsrq.fileviewutils.entity.SysUserWithRole;
import top.xzxsrq.fileviewutils.exception.BusinessException;
import top.xzxsrq.web.utils.AjaxResult;
import top.xzxsrq.web.utils.AuthExceptionUtil;
import top.xzxsrq.web.utils.JwtTokenUtil;
import top.xzxsrq.web.utils.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 验证用户名密码正确后 生成一个token并将token返回给客户端
 */
@Log4j2
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private AuthenticationManager authenticationManager;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        // 设置登录URL
        super.setFilterProcessesUrl("/user/login");
    }


    /**
     * 验证操作 接收并解析用户凭证
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (!HttpMethod.POST.name().equals(request.getMethod().toUpperCase())) {
            throw new BusinessException("只支持POST");
        }
        SysUser body = WebUtils.getBody(request, SysUser.class);
        // 从输入流中获取到登录的信息
        // 创建一个token并调用authenticationManager.authenticate() 让Spring security进行验证
        // 需要再spring容器里面添加一个org.springframework.security.core.userdetails.UserDetailsService的实现类
        // 这里Spring security就去调用这个现实类的loadUserByUsername方法进行查询验证
        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(body.getUsername(), body.getPassword()));
    }

    /**
     * 验证【成功】后调用的方法
     * 若验证成功 生成token并返回
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException {
        SysUserWithRole user = (SysUserWithRole) authResult.getPrincipal();
        // 创建Token
        String token = JwtTokenUtil.createToken(user);
        // 设置编码 防止乱码问题
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        Map<String, String> su = new HashMap<>();
        su.put("token", JwtTokenUtil.TOKEN_PREFIX + token);
        WebUtils.renderString(response, AjaxResult.success(su));
    }

    /**
     * 验证【失败】调用的方法
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        AjaxResult errMsgByExceptionType = AuthExceptionUtil.getErrMsgByExceptionType(failed);
        WebUtils.renderString(response, errMsgByExceptionType);
    }
}
